namespace EGame {
  export interface IJob {
    id: number;
    work(): Promise<any>;
  }
  export class Work {
    private static UUID = 0;
    private _isWork = false;
    private _id = 0;
    constructor(private _jobs: IJob[], private _observer: Observer) {
      this._id = Work.UUID++;
    }

    get id() {
      return this._id;
    }

   async notify() {
      if (this._isWork) return;
      this._work();
    }

    protected async _work(): Promise<void> {
      const job = this._jobs.shift();
      if (job) {
        this._isWork = true;
        const result = await job.work();
        this._isWork = false;
        this._observer.emit("worked", { job, result });
        this._work();
      }
    }
  }

  export class WorkManager {
    removeWorkedListener(callback: (e: { job: IJob; result: any; }) => void) {
      this._observer.remove('worked',callback)
    }
    private _observer = new Observer();
    private static uuid = 0;
    private _works: Work[] = [];
    private _jobs: IJob[] = [];
    constructor(workCount: number) {
      for (let i = 0; i < workCount; i++) {
        this._works.push(new Work(this._jobs, this._observer));
      }
    }

    get workerCount() {
      return this._works.length;
    }

    addWorkedListener(listener: (e: {job: IJob, result: any}) => void) {
      this._observer.on('worked', listener);
    }

    receive(job: IJob) {
      this._jobs.push(job);
      this._works.forEach((work) => work.notify());
    }
  }
}
